package job

import (
	"context"
	"time"
)

const (
	DefaultRetryJetLag  = time.Second
	DefaultRetryTimeout = 2 * time.Second
	DefaultRetryCount   = 3
)

// RetryJetLagFunc 定义重试的时间策略
type RetryJetLagFunc func(ctx context.Context, retryCount int, lastTime time.Duration) time.Duration

func RetryJetLagAlways(ctx context.Context, retryCount int, lastTime time.Duration) time.Duration {
	return DefaultRetryJetLag
}

// IsRetryFunc 是否进行重试
type IsRetryFunc func(ctx context.Context, retryCount int, err error) bool

func RetryAlways(ctx context.Context, retryCount int, err error) bool {
	return true
}

func WithRetry(ctx context.Context, handler func(ctx context.Context) error, opts ...RetryOptions) error {
	options := NewOptions(opts...)
	//判断程序是否设置超时
	_, ok := ctx.Deadline()
	if !ok {
		var cancelFunc context.CancelFunc
		ctx, cancelFunc = context.WithTimeout(ctx, options.timeout)
		defer cancelFunc()
	}
	var (
		herr        error
		retryJetLag time.Duration
		ch          = make(chan error, 1)
	)
	for i := 0; i < options.retryNums; i++ {
		go func() {
			err := handler(ctx)
			ch <- err
		}()
		select {
		case herr = <-ch:
			if herr == nil {
				return nil
			}
			if !options.isRetryFunc(ctx, i, herr) {
				return herr
			}
			//重试
			options.retryJetLagFunc(ctx, i, retryJetLag)
			time.Sleep(retryJetLag)
		case <-ctx.Done():
			return ctx.Err()
		}
	}
	return herr
}
